/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ts, native, HomeCheckHint } from "qvog-dsl";

const SEVERITY = 2;
const DOC_PATH = 'docs/function-variable-dsl.md';
const DESCRIPTION = 'Variables used within a function should be passed as parameters rather than accessed directly from the global scope.';
const symbolTable: Map<string, ts.Node> = new Map();

export default native<HomeCheckHint>()()
    .match((node): node is ts.Identifier => {
        if (ts.isSourceFile(node)) {
            symbolTable.clear();
            buildSymbolTable(node.getSourceFile());
        }
        return ts.isIdentifier(node);
    })
    .when((node): boolean => {
        if (ts.isVariableDeclaration(node.parent) || ts.isFunctionDeclaration(node.parent)) {
            return false;
        }
        if (!node.escapedText || !symbolTable.get(node.escapedText!)) {
            return false;
        }
        return inFunction(node) && isGlobalVariable(symbolTable.get(node.escapedText!)!);
    })
    .report({
        severity: SEVERITY,
        description: DESCRIPTION,
        docPath: DOC_PATH,
    });


function buildSymbolTable(sourceFile: ts.SourceFile): void {
    const visit: (node: ts.Node) => void = (node: ts.Node) => {
        if (ts.isVariableDeclaration(node)) {
            const name = node.name.getText();
            // 存储变量声明本身，而不仅仅是初始化器
            symbolTable.set(name, node);
        } else if (ts.isFunctionDeclaration(node)) {
            const name = node.name?.getText();
            if (name) {
                symbolTable.set(name, node);
            }
            // 处理函数参数
            if (node.parameters) {
                buildSymbolTableAddParam(node.parameters);
            }
        } else if (ts.isClassDeclaration(node)) {
            const name = node.name?.getText();
            if (name) {
                symbolTable.set(name, node);
            }
        } else if (ts.isArrowFunction(node)) {
            const parent = node.parent;
            if (ts.isVariableDeclaration(parent)) {
                const name = parent.name.getText();
                symbolTable.set(name, node);
            }
            // 处理箭头函数参数
            if (node.parameters) {
                buildSymbolTableAddParam(node.parameters);
            }
        }

        // 递归遍历子节点
        ts.forEachChild(node, visit);
    };

    visit(sourceFile);
}

function buildSymbolTableAddParam(parameters: ts.NodeArray<ts.ParameterDeclaration>): void {
    for (const param of parameters) {
        if (ts.isParameter(param) && ts.isIdentifier(param.name)) {
            symbolTable.set(param.name.text, param);
        }
    }
}

function inFunction(node: ts.Node) {
    let current = node as ts.Node;
    while (current.parent) {
        current = current.parent;
        if (ts.isFunctionDeclaration(current)) {
            return true;
        }
    }
    return false;
}

function isGlobalVariable(node: ts.Node) {
    return ts.isVariableDeclaration(node) && 
    ts.isVariableDeclarationList(node.parent) &&
    ts.isVariableStatement(node.parent.parent) &&
    ts.isSourceFile(node.parent.parent.parent);
}
